iT邦幫忙

2026 iThome 鐵人賽

DAY 27
0
Claude AI

Claude × Playwright:從新進同事到 Agentic SDET 代理人系列 第 27 篇

Day 27|本機測試都通過了,放到 CI 卻失敗

  • 分享至 

  • xImage
  •  

前言

Day 26 的測試跑在你的機器上,也就只有你看得到。今天把它推進 GitHub Actions,每次 push 都跑;同時把紅燈變成別人也讀得懂的東西:report、trace、screenshot 都要撈得回來。

為什麼測試一定要進 CI

本機跑測試有兩個問題,而且都不是「麻煩」而已。

第一,它只在你想到的時候跑。改完一段程式碼、趕著下班的那次,通常就是最該跑的那次。

第二,也是更嚴重的,本機的綠燈不能拿來說服任何人。你說「我這邊是綠的」,對方無從查證,也無從重播。CI 的價值不只是自動化,是它產生了一份別人可以檢視的紀錄。

但只把測試搬上去還不夠。CI 紅了,你能拿到的預設訊息可能只有一行 exit code 1,或一段沒有上下文的逾時。那種紅燈跟沒有紅燈差不多,因為沒有人能從它往下走。

所以這一天有兩件事:把測試接上去,以及讓紅燈自帶證據。第二件事的規格從 Day 8 到 Day 11 就立好了,今天是把它搬進 pipeline。

實驗:兩個環境用 matrix 一起跑

workflow 的核心是 matrix,同一份測試碼同時跑兩個環境:

name: e2e
on:
  push:
    branches: ['**']
    paths: ['tests/**', 'package.json', '.github/workflows/e2e.yml']
  pull_request:
  workflow_dispatch:
    inputs:
      repeat_flaky:
        description: '額外跑 flaky 量測(重複次數,0 = 不跑)'
        default: '0'

concurrency:
  group: e2e-${{ github.ref }}
  cancel-in-progress: true

jobs:
  e2e:
    name: e2e (${{ matrix.sut }})
    strategy:
      fail-fast: false
      matrix:
        sut: [clean, with-bugs]

fail-fast: false 是關鍵。預設值會在第一格紅掉時取消其他格,那正好毀掉這條 pipeline 的用途:我們要的就是「clean 綠、with-bugs 紅」這個對照,少一邊就判不出來。

實跑五次的結果:

run e2e (clean) e2e (with-bugs)
30710272941 ✗ 紅 ✗ 紅
30710272941(rerun --failed) ✗ 紅 ✗ 紅
30710677728 ✓ 3 passed ✗ 紅
30710750654 ✓ 未跑
30710799963 ✓ 未跑

CI 流程從三種觸發、matrix 兩格、到 if always 上傳 artifact,下方是五次實跑的結果表與兩條拉證據的指令

with-bugs 一路紅是設計,它紅在斷言(Received: 0),不是紅在別的地方。clean 前兩次紅不是設計,那是真的踩到坑,整個過程是 Day 28 的主角。

順帶一提,clean 是 3 passed 不是 4:登入測試在 CI 上自動 skip,因為 repo 沒設帳密 secrets。祕密不落地的代價是少跑一支,這個取捨要寫在明處,不要讓人以為測試少了一支是漏掉。

讀紅燈:artifact 是契約

紅燈能不能往下走,取決於 CI 有沒有把證據留下來。上傳 artifact 的條件是 if: always(),因為紅的時候才最需要它。

真正的重點在名稱。artifact 名不是隨手取的,是一份契約:下游要靠名稱才分得出這次紅的是哪一層。UI 測試與 API 測試的結果分開命名,否則解析的人拿到一包混在一起的檔案,只能自己猜。

改名要連契約表一起改,這條跟 Day 11 的證據包命名是同一個道理:能被自動處理的前提是名字可預測。

拉證據的指令固定兩條:

gh run view <run-id> --log-failed     # 只看失敗那幾行
gh run download <run-id>              # 把 artifact 撈下來

--log-failed 那條很重要。整份 CI log 動輒幾萬行,全讀進 context 是純粹的浪費,而失敗訊息就那幾十行。

幾個把 pipeline 弄慢或弄糊的設計

觸發時機分三種,不要混成一種。 PR 要快,跑必要範圍;nightly 跑全量;workflow_dispatch 留給手動重跑與參數化實驗,本專案就是拿它做 flaky 量測。

API 測試切成獨立 job,而且不裝瀏覽器。 安裝瀏覽器通常是整條裡最慢的一步,API 測試不需要它。讓後端規則的紅燈在幾十秒內先亮,比等 UI 測試跑完十分鐘才知道有價值。

但這裡有個坑:UI job 掛 needs: api-test 只能用在 PR。 nightly 也掛的話,API 一紅整批 UI 會變成 skipped,隔天早上你看到的是一份缺一半的報告,而不是壞消息。

祕密只走 GitHub secrets,不寫死在 workflow、不進知識庫。

改 workflow 是副作用,先把 diff 完整列給人看,同意才寫檔。

小結

先盤點專案的測試指令與設定,決定三種觸發時機;用 matrix 讓同一份測試碼跑兩個環境,fail-fast: false 保住對照;跑完不論綠紅都上傳 artifact,名稱照契約;紅了用 --log-failed 只讀失敗那幾行,需要證據再 download 撈下來;最後把結論交給下一天的分流。

        push / PR / 手動
                │
                ▼
        盤點測試指令與設定
                │
                ▼
      matrix: [clean, with-bugs]
        fail-fast: false
          │            │
          ▼            ▼
     e2e (clean)  e2e (with-bugs)
          │            │
          └──────┬─────┘
                 ▼
     if: always() 上傳 artifact
     (名稱照契約,UI 與 API 分開)
                 │
          ┌──────┴──────┐
        全綠           有紅
          │              │
          ▼              ▼
        收工      gh run view --log-failed
                  gh run download
                         │
                         ▼
                  交給明天的分流

CI 的價值是產生別人可以檢視的紀錄,不只是自動跑。fail-fast: false 保住的是判斷力,兩個環境的對照少一邊,就分不出測試的錯與產品的錯。而 artifact 名是契約,紅燈能不能往下走,取決於證據撈不撈得回來、分不分得出是哪一層。

下一步

CI 紅了。第一個反應通常是打開測試碼開始改,改到它變綠為止。那是最貴的錯。

明天先分流再動手。手上有一組乾淨的對照:同一份測試碼、同一台機器、同一分鐘,只換受測環境。看它在哪一邊紅,就分得出是測試自己壞掉,還是產品真的回歸。而上面那兩次 clean 的紅燈,會示範這個判斷可以錯得多離譜,以及是什麼把它拉回來。


上一篇
第 26 天|替購物車寫支自動化測試
下一篇
第 28 天|測試在 CI 上失敗,我連續猜錯兩次原因
系列文
Claude × Playwright:從新進同事到 Agentic SDET 代理人 共 28 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言